Read in MoTR Data
# Interesting way of reading multiple files in one directory!
file_prefix = "../reading_measures/cleaned_f160_b40"
fnames = list.files(path=file_prefix)
# fnames: [1] "reader_55_reading_measures.csv" "reader_56_reading_measures.csv"
# [3] "reader_59_reading_measures.csv" "reader_61_reading_measures.csv"
# Read in the data
df = data.frame()
# uses the mutate() function from the dplyr package to create a new column in a data frame called subj. The new column is created by removing the substring _reading_measures.csv from the values in an existing column called f.
for (f in fnames) {
temp = read.csv(paste0(file_prefix, "/", f)) %>%
mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration,
gaze_duration, go_pass_time, FPReg, subj) %>%
rename(go_past_time = go_pass_time)
df = rbind(df, temp)
}
df
# `gather will take column 6-9's column name make it rows, name 'metric', take their value as another row`
motr_df = df %>%
mutate(expr_id = if_else(expr_id == 1, "Attachment", "Provo")) %>%
gather(metric, value, 6:10)
motr_df
NA
# Average across subjects
motr_agg_df = motr_df %>%
group_by(expr_id, cond_id, para_nr, word, word_nr, metric) %>%
summarise(value = mean(value)) %>% # sum up four subjects for each metric and divide by 4.
ungroup() %>%
arrange(expr_id, cond_id, para_nr, word_nr) # like sort in python
`summarise()` has grouped output by 'expr_id', 'cond_id', 'para_nr', 'word', 'word_nr'. You can override using the `.groups` argument.
#View(motr_agg_df)
motr_agg_df
table(motr_df$subj)
reader_59 reader_61 reader_62 reader_63
3100 3100 3100 3100
# reader_55 reader_56 reader_59 reader_61
# 3100 3100 3100 3100
motr_provo_df = motr_agg_df %>%
filter(expr_id == "Provo") %>%
rename(text_id = para_nr,
word_text_idx = word_nr,
motr_value = value) %>%
dplyr::select(-expr_id, -cond_id)
motr_provo_df
NA
Comparison to Provo
# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("../ancillary_data/provo_df.csv") %>%
dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
rename(word_idx = trigger_idx)
provo_modeling_df
NA
# Read in Provo eyetracking data
provo_raw_df = read.csv("../ancillary_data/provo_eyetracking.csv")
provo_eyetracking_df = provo_raw_df %>%
dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, IA_ID, Word,IA_FIRST_FIXATION_DURATION, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT) %>%
rename( first_duration = IA_FIRST_FIXATION_DURATION, # whether it is first pass?
gaze_duration = IA_FIRST_RUN_DWELL_TIME,
total_duration = IA_DWELL_TIME,
go_past_time = IA_REGRESSION_PATH_DURATION,
FPReg = IA_REGRESSION_OUT,
subj = Participant_ID,
text_id = Text_ID,
sent_id = Sentence_Number,
word_idx = Word_In_Sentence_Number,
word_text_idx = IA_ID,
word = Word,
ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>% # notice:average across subj, binary(0,1) becomes float.
mutate(gaze_duration = ifelse(ff_progressive == 0, 0, gaze_duration),
go_past_time = ifelse(ff_progressive == 0, 0, go_past_time)) %>%
dplyr::select(-ff_progressive) %>%
gather(metric, value, 7:11) %>% # not include FFReg which is in column 11
mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
drop_na() %>% # actually, drop first word in a sentence
group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
summarise(value = mean(value)) %>%
ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)
provo_eyetracking_df
NA
provo_df = merge(provo_eyetracking_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
arrange(text_id, sent_id, word_idx)
provo_df
provo_df = merge(provo_df, motr_provo_df, by=c("text_id", "word_text_idx", "metric")) %>%
rename(eyetr_value = value) %>%
arrange(text_id, sent_id, word_idx) %>%
filter(word.x == word) %>% #word.y has no captical word
dplyr::select(-word.x, -word.y) %>%
mutate(motr_outlier = if_else(motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
filter(motr_outlier == F) %>% # clear outlier -> 13 was filtered.
gather(measure, value, c("eyetr_value", "motr_value")) %>%
# filter(metric != "first_duration") %>%
filter(metric != "FPReg")
# View(provo_df)
provo_df %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
facet_wrap(.~measure) +
xlab("Reading Measure")

# ggsave("../visualization/density.png", device = "png", width = 6, height = 2.5)
provo_df %>%
filter(measure == "motr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for motr")

provo_df %>%
filter(measure == "eyetr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for eyetr")

gd_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
View(gd_df)
cor.test(gd_df$eyetr_value, gd_df$motr_value) #cor rise from 0.529 to 0.5989
Pearson's product-moment correlation
data: gd_df$eyetr_value and gd_df$motr_value
t = 16, df = 375, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.5721 0.6928
sample estimates:
cor
0.6364
provo_df %>%
spread(measure, value) %>%
ggplot(aes(x = motr_value, y=eyetr_value, color=metric)) +
geom_point(alpha = 0.2) +
facet_wrap(.~metric, scales="free") +
geom_smooth()

# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)
gd_df_2 = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
cor.test(gd_df_2$eyetr_value, gd_df_2$motr_value)
Pearson's product-moment correlation
data: gd_df_2$eyetr_value and gd_df_2$motr_value
t = 15, df = 378, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.5577 0.6812
sample estimates:
cor
0.6233
gd_df_3 = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
cor.test(gd_df_3$eyetr_value, gd_df_3$motr_value)
Pearson's product-moment correlation
data: gd_df_3$eyetr_value and gd_df_3$motr_value
t = 17, df = 378, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6008 0.7145
sample estimates:
cor
0.6614
gd_df_4 = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
cor.test(gd_df_4$eyetr_value, gd_df_4$motr_value)
Pearson's product-moment correlation
data: gd_df_4$eyetr_value and gd_df_4$motr_value
t = 13, df = 370, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.4757 0.6178
sample estimates:
cor
0.5507
provo_df %>%
gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
filter(metric == "gaze_duration") %>%
ggplot(aes(x = value, y=word_prop_val, color = measure)) +
geom_point(alpha = 0.2) +
facet_grid(word_prop~measure, scales="free") +
geom_smooth() +
xlab("Reading Measure")

# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
ggplot(aes(x = value, y=freq, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

provo_df %>%
ggplot(aes(x = value, y=surp, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

Shape of surprisal / RT relationship
fit_gam_inner = function(bootstrap_sample, mean_predictors) {
df = bootstrap_sample$data
weights = tabulate(as.integer(bootstrap_sample), nrow(df))
m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
terms_to_predict = c("s(surp)", "s(prev_surp)")
newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
len=mean_predictors$freq, prev_len=mean_predictors$freq)
# Returns a matrix N_samples * N_terms.
per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")
# Additive model -- sum across predictor response contributions (matrix columns).
predictions = rowSums(per_term_predictions)
return(newdata %>% mutate(y=predictions))
}
fit_gam = function(df, mean_predictors, alpha=0.05) {
# Bootstrap-resample data
boot_models = df %>% bootstraps(times=10) %>%
# Fit a GAM and get predictions for each sample
mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
# Extract mean and 5% and 95% percentile y-values for each surprisal value
result = boot_models %>%
unnest(smoothed) %>%
dplyr::select(surp, y) %>%
group_by(surp) %>%
summarise(y_lower=quantile(y, alpha / 2),
y_upper=quantile(y, 1 - alpha / 2),
y=mean(y)) %>%
ungroup()
return (result)
}
gam_modeling_df = provo_df %>%
spread(measure, value) %>%
mutate(len = nchar(word)) %>%
group_by(metric, text_id) %>%
arrange(word_text_idx) %>%
mutate(prev_surp = lag(surp),
prev_freq = lag(freq),
prev_len = lag(len),
prev_eyetr_value = lag(eyetr_value)) %>%
ungroup() %>%
drop_na() %>%
rename(psychometric = motr_value)
smooths_df = data.frame()
metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
print(paste0("Fitting model for ", m))
dummy_df = gam_modeling_df %>% filter(metric == m)
mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
smooths = dummy_df %>% fit_gam(., mean_predictors)
#Fix 0 surprisal = 0 ms
gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"
# Surprisal curves
ggplot() +
geom_line(data = smooths_df, aes(x=surp, y=y, color = psychometric), size=0.7) +
geom_ribbon(data = smooths_df, aes(x=surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
scale_x_continuous(labels=c(0, 10, 20), breaks=c(0, 10, 20), minor_breaks = NULL) +
facet_wrap(psychometric~.) +
ylab("Slowdown due to Surprisal (ms)") +
xlab("Surprisal of Word") +
ggtitle("Relationship between MoTR Times and Surprisal")

theme(
legend.position = "none",
panel.grid.minor = element_blank()
)
List of 2
$ legend.position : chr "none"
$ panel.grid.minor: list()
..- attr(*, "class")= chr [1:2] "element_blank" "element"
- attr(*, "class")= chr [1:2] "theme" "gg"
- attr(*, "complete")= logi FALSE
- attr(*, "validate")= logi TRUE
Targeted Evaluation Data
motr_attach_df = motr_df %>%
filter(expr_id == "Attachment") %>%
rename( item_id = para_nr) %>%
mutate(item_id = as.integer(item_id)) %>%
mutate(cond_id = as.factor(cond_id)) %>%
mutate(cond_id = if_else(cond_id == 1, "No Comma", "Comma")) %>%
filter(metric != "FPReg") %>%
filter(! (item_id == 4 & cond_id == "No Comma") ) %>% # just because of alignment issues for now
mutate(crit = if_else(word == "and", word_nr, as.integer(0) )) %>%
group_by(cond_id, item_id) %>%
mutate(crit = unique(crit)[2]) %>%
ungroup() %>%
mutate(word_nr = word_nr - crit)
agg_motr_attach_df = motr_attach_df %>%
filter(word_nr >= -2, word_nr < 6) %>%
group_by(cond_id, word_nr, metric) %>%
summarise( m = mean(value),
sd = std.error(value),
upper = m + 1.96 * sd,
lower = m - 1.98 * sd,
n = n()) %>%
ungroup()
`summarise()` has grouped output by 'cond_id', 'word_nr'. You can override using the `.groups` argument.
agg_motr_attach_df %>%
ggplot(aes(x = word_nr, y = m, color = cond_id)) +
geom_rect(aes(xmin = 2.5, xmax = 5.5, ymin = 100, ymax = 800), fill=alpha("white", 0), color = "#45ef70", linetype = "dotted") +
geom_point() +
geom_errorbar(aes(ymax = upper, ymin = lower), width = 0.3) +
geom_line() +
#geom_text(aes(label = word, y = if_else(cond_id == "Comma", 3000, 3500)), size = 2) +
#facet_grid(para_nr~cond_id) +
ylab("Reading Time") +
xlab("Condition") +
scale_x_continuous(breaks=-2:5, labels=c("the", "man", "and", "his", "wife", "ran", "away", "from")) +
facet_grid(~metric) +
theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1)
)

#ggsave("../visualization/attachment.png", device = "png", width = 6, height = 3)
options(JULIA_HOME = "/Applications/Julia-1.8.app/Contents/Resources/julia/bin/")
# library(jglmm)
# jglmm_setup()
attach_lm_df = motr_attach_df %>%
filter(metric == "gaze_duration") %>%
filter(word_nr == 3) %>%
mutate(item_id = as.factor(item_id),
subj = as.factor(subj))
m = attach_lm_df %>%
lmer(value ~ cond_id + (cond_id | item_id) + (cond_id | subj), data=.)
boundary (singular) fit: see help('isSingular')
Warning: Model failed to converge with 1 negative eigenvalue: -1.6e-03
summary(m)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: value ~ cond_id + (cond_id | item_id) + (cond_id | subj)
Data: .
REML criterion at convergence: 825.2
Scaled residuals:
Min 1Q Median 3Q Max
-1.121 -0.984 -0.205 0.638 3.783
Random effects:
Groups Name Variance Std.Dev. Corr
item_id (Intercept) 1.44e+03 3.79e+01
cond_idNo Comma 1.74e+02 1.32e+01 1.00
subj (Intercept) 0.00e+00 0.00e+00
cond_idNo Comma 2.27e-12 1.51e-06 NaN
Residual 7.70e+04 2.77e+02
Number of obs: 60, groups: item_id, 15; subj, 4
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 282.7 50.8 7.0 5.56 0.00085 ***
cond_idNo Comma 23.7 75.5 12.6 0.31 0.75882
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Correlation of Fixed Effects:
(Intr)
cond_idNCmm -0.673
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzYCBpcyBhIGZ1bmN0aW9uIGluIFIgdGhhdCBzdXBwcmVzc2VzIHRoZSBzdGFydHVwIG1lc3NhZ2VzIHRoYXQgYXJlIGdlbmVyYXRlZCB3aGVuIGxvYWRpbmcgYSBwYWNrYWdlLiBJdCBjYW4gYmUgdXNlZnVsIHRvIHByZXZlbnQgdGhlIGNvbnNvbGUgZnJvbSBiZWluZyBjbHV0dGVyZWQgd2l0aCBtZXNzYWdlcyB3aGVuIHlvdSBhcmUgbG9hZGluZyBtdWx0aXBsZSBwYWNrYWdlcyBvciB3aGVuIHlvdSBkb24ndCB3YW50IHRvIHNlZSB0aGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzLgpzaGhoIDwtIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyAjIEl0J3MgYSBsaWJyYXJ5LCBzbyBzaGhoIQoKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKIyBgdGhlbWVfc2V0KHRoZW1lX2J3KCkpYCBpcyBhIGNvbW1hbmQgaW4gUiB0aGF0IHNldHMgdGhlIGRlZmF1bHQgdGhlbWUgZm9yIGFsbCBzdWJzZXF1ZW50IHBsb3RzIHRvIGEgdGhlbWUgY2FsbGVkIGB0aGVtZV9idygpYC4KIyBgdGhlbWVfYncoKWAgaXMgYSBidWlsdC1pbiB0aGVtZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgYSBjbGFzc2ljIGJsYWNrLWFuZC13aGl0ZSB0aGVtZSB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIEJ5IHNldHRpbmcgdGhlIGRlZmF1bHQgdGhlbWUgdG8gYHRoZW1lX2J3KClgLCBhbGwgcGxvdHMgdGhhdCBhcmUgY3JlYXRlZCB1c2luZyBnZ3Bsb3QyIHdpbGwgaGF2ZSB0aGlzIHRoZW1lIHVubGVzcyBhIGRpZmZlcmVudCB0aGVtZSBpcyBleHBsaWNpdGx5IHNwZWNpZmllZC4KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGBvcHRpb25zKGRpZ2l0cz00KWAgaXMgYSBjb21tYW5kIGluIFIgdGhhdCBzZXRzIHRoZSBudW1iZXIgb2YgZGlnaXRzIHRvIGRpc3BsYXkgd2hlbiBwcmludGluZyBudW1lcmljIHZhbHVlcy4KCiMgQnkgZGVmYXVsdCwgUiB3aWxsIHByaW50IHVwIHRvIDcgZGlnaXRzIGZvciBudW1lcmljIHZhbHVlcy4gSG93ZXZlciwgeW91IGNhbiB1c2UgdGhlIGBvcHRpb25zKClgIGZ1bmN0aW9uIHRvIGNoYW5nZSB0aGlzIGJlaGF2aW9yLiBJbiB0aGlzIGNhc2UsIGBvcHRpb25zKGRpZ2l0cz00KWAgc2V0cyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyB0byA0LCB3aGljaCBtZWFucyB0aGF0IGFsbCBudW1lcmljIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZCB3aXRoIGF0IG1vc3QgNCBkaWdpdHMuCm9wdGlvbnMoZGlnaXRzPTQpCnNldC5zZWVkKDQ0NCkKCiMgVGhlIGBwaXBlX21lc3NhZ2VgIGZ1bmN0aW9uIGZpcnN0IG91dHB1dHMgdGhlIHN0YXR1cyBtZXNzYWdlIHVzaW5nIHRoZSBtZXNzYWdlKCkgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgdHJhY2tpbmcgdGhlIHByb2dyZXNzIG9mIGEgbG9uZyBjb21wdXRhdGlvbiBvciBmb3IgcHJvdmlkaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB1c2VyLgojIEFmdGVyIHByaW50aW5nIHRoZSBtZXNzYWdlLCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgLmRhdGEgYXJndW1lbnQgdW5jaGFuZ2VkLiBUaGlzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCBhcyBhICJwaXBlIiBpbiBhIGRhdGEgcHJvY2Vzc2luZyBwaXBlbGluZSwgd2hlcmUgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gaXMgdXNlZCBhcyB0aGUgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uCnBpcGVfbWVzc2FnZSA9IGZ1bmN0aW9uKC5kYXRhLCBzdGF0dXMpIHttZXNzYWdlKHN0YXR1cyk7IC5kYXRhfQoKYGBgCgpgYGB7cn0KCmBgYAoKIyBSZWFkIGluIE1vVFIgRGF0YQoKYGBge3J9CiMgSW50ZXJlc3Rpbmcgd2F5IG9mIHJlYWRpbmcgbXVsdGlwbGUgZmlsZXMgaW4gb25lIGRpcmVjdG9yeSEKCmZpbGVfcHJlZml4ID0gIi4uL3JlYWRpbmdfbWVhc3VyZXMvY2xlYW5lZF9mMTYwX2I0MCIKZm5hbWVzID0gbGlzdC5maWxlcyhwYXRoPWZpbGVfcHJlZml4KQojIGZuYW1lczogWzFdICJyZWFkZXJfNTVfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNTZfcmVhZGluZ19tZWFzdXJlcy5jc3YiCiMgWzNdICJyZWFkZXJfNTlfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNjFfcmVhZGluZ19tZWFzdXJlcy5jc3YiCgojIFJlYWQgaW4gdGhlIGRhdGEKZGYgPSBkYXRhLmZyYW1lKCkKCiMgdXNlcyB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGluIGEgZGF0YSBmcmFtZSBjYWxsZWQgc3Viai4gVGhlIG5ldyBjb2x1bW4gaXMgY3JlYXRlZCBieSByZW1vdmluZyB0aGUgc3Vic3RyaW5nIF9yZWFkaW5nX21lYXN1cmVzLmNzdiBmcm9tIHRoZSB2YWx1ZXMgaW4gYW4gZXhpc3RpbmcgY29sdW1uIGNhbGxlZCBmLgpmb3IgKGYgaW4gZm5hbWVzKSB7CiAgdGVtcCA9IHJlYWQuY3N2KHBhc3RlMChmaWxlX3ByZWZpeCwgIi8iLCBmKSkgJT4lCiAgICBtdXRhdGUoc3ViaiA9IHN0cl9yZW1vdmUoZiwgIl9yZWFkaW5nX21lYXN1cmVzLmNzdiIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgZmlyc3RfZHVyYXRpb24sIHRvdGFsX2R1cmF0aW9uLAogICAgICAgICAgICAgICAgICBnYXplX2R1cmF0aW9uLCBnb19wYXNzX3RpbWUsIEZQUmVnLCBzdWJqKSAlPiUKICAgIHJlbmFtZShnb19wYXN0X3RpbWUgPSBnb19wYXNzX3RpbWUpCiAgZGYgPSByYmluZChkZiwgdGVtcCkKfQpkZgoKIyBgZ2F0aGVyIHdpbGwgdGFrZSBjb2x1bW4gNi05J3MgY29sdW1uIG5hbWUgbWFrZSBpdCByb3dzLCBuYW1lICdtZXRyaWMnLCB0YWtlIHRoZWlyIHZhbHVlIGFzIGFub3RoZXIgcm93YAptb3RyX2RmID0gZGYgJT4lCiAgbXV0YXRlKGV4cHJfaWQgPSBpZl9lbHNlKGV4cHJfaWQgPT0gMSwgIkF0dGFjaG1lbnQiLCAiUHJvdm8iKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIDY6MTApCgptb3RyX2RmCgpgYGAKCmBgYHtyfQojIEF2ZXJhZ2UgYWNyb3NzIHN1YmplY3RzCm1vdHJfYWdnX2RmID0gbW90cl9kZiAlPiUKICBncm91cF9ieShleHByX2lkLCBjb25kX2lkLCBwYXJhX25yLCB3b3JkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JSAgICAjIHN1bSB1cCBmb3VyIHN1YmplY3RzIGZvciBlYWNoIG1ldHJpYyBhbmQgZGl2aWRlIGJ5IDQuCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZF9ucikgICAjIGxpa2Ugc29ydCBpbiBweXRob24gCgojVmlldyhtb3RyX2FnZ19kZikKCm1vdHJfYWdnX2RmCgp0YWJsZShtb3RyX2RmJHN1YmopCiMgcmVhZGVyXzU1IHJlYWRlcl81NiByZWFkZXJfNTkgcmVhZGVyXzYxIAojICAgIDMxMDAgICAgICAzMTAwICAgICAgMzEwMCAgICAgIDMxMDAKCmBgYAoKYGBge3J9Cgptb3RyX3Byb3ZvX2RmID0gbW90cl9hZ2dfZGYgJT4lCiAgZmlsdGVyKGV4cHJfaWQgPT0gIlByb3ZvIikgJT4lCiAgcmVuYW1lKHRleHRfaWQgPSBwYXJhX25yLAogICAgICAgICB3b3JkX3RleHRfaWR4ID0gd29yZF9uciwKICAgICAgICAgbW90cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1leHByX2lkLCAtY29uZF9pZCkKCm1vdHJfcHJvdm9fZGYKCmBgYAoKCgojIENvbXBhcmlzb24gdG8gUHJvdm8KCmBgYHtyfQojIFJlYWQgaW4gUHJvdm8gc3VycHJpc2FsLCBmcmVxdWVuY3kgYW5kIGxlbmd0aCBkYXRhCnByb3ZvX21vZGVsaW5nX2RmID0gcmVhZC5jc3YoIi4uL2FuY2lsbGFyeV9kYXRhL3Byb3ZvX2RmLmNzdiIpICU+JQogIGRwbHlyOjpzZWxlY3QodGV4dF9pZCwgc2VudF9pZCwgdHJpZ2dlcl9pZHgsIHdvcmQsIGZyZXEsIHN1cnAsIGxlbikgJT4lCiAgcmVuYW1lKHdvcmRfaWR4ID0gdHJpZ2dlcl9pZHgpCgpwcm92b19tb2RlbGluZ19kZgoKYGBgCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIGV5ZXRyYWNraW5nIGRhdGEKCnByb3ZvX3Jhd19kZiA9IHJlYWQuY3N2KCIuLi9hbmNpbGxhcnlfZGF0YS9wcm92b19leWV0cmFja2luZy5jc3YiKQoKcHJvdm9fZXlldHJhY2tpbmdfZGYgPSBwcm92b19yYXdfZGYgJT4lCiAgZHBseXI6OnNlbGVjdChQYXJ0aWNpcGFudF9JRCwgVGV4dF9JRCwgU2VudGVuY2VfTnVtYmVyLCBXb3JkX0luX1NlbnRlbmNlX051bWJlciwgSUFfSUQsIFdvcmQsSUFfRklSU1RfRklYQVRJT05fRFVSQVRJT04sIElBX0ZJUlNUX0ZJWF9QUk9HUkVTU0lWRSwgSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsIElBX0RXRUxMX1RJTUUsIElBX1JFR1JFU1NJT05fUEFUSF9EVVJBVElPTiwgSUFfUkVHUkVTU0lPTl9PVVQpICU+JQogIHJlbmFtZSggZmlyc3RfZHVyYXRpb24gPSBJQV9GSVJTVF9GSVhBVElPTl9EVVJBVElPTiwgICAgIyB3aGV0aGVyIGl0IGlzIGZpcnN0IHBhc3M/CiAgICAgICAgICBnYXplX2R1cmF0aW9uID0gSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsCiAgICAgICAgICB0b3RhbF9kdXJhdGlvbiA9IElBX0RXRUxMX1RJTUUsCiAgICAgICAgICBnb19wYXN0X3RpbWUgPSBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sCiAgICAgICAgICBGUFJlZyA9IElBX1JFR1JFU1NJT05fT1VULAogICAgICAgICAgc3ViaiA9IFBhcnRpY2lwYW50X0lELAogICAgICAgICAgdGV4dF9pZCA9IFRleHRfSUQsCiAgICAgICAgICBzZW50X2lkID0gU2VudGVuY2VfTnVtYmVyLAogICAgICAgICAgd29yZF9pZHggPSBXb3JkX0luX1NlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfdGV4dF9pZHggPSBJQV9JRCwKICAgICAgICAgIHdvcmQgPSBXb3JkLAogICAgICAgICAgZmZfcHJvZ3Jlc3NpdmUgPSBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUpICU+JSAjIG5vdGljZTphdmVyYWdlIGFjcm9zcyBzdWJqLCBiaW5hcnkoMCwxKSBiZWNvbWVzIGZsb2F0LgogIG11dGF0ZShnYXplX2R1cmF0aW9uID0gaWZlbHNlKGZmX3Byb2dyZXNzaXZlID09IDAsIDAsIGdhemVfZHVyYXRpb24pLAogICAgICAgICBnb19wYXN0X3RpbWUgPSBpZmVsc2UoZmZfcHJvZ3Jlc3NpdmUgPT0gMCwgMCwgZ29fcGFzdF90aW1lKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZmZfcHJvZ3Jlc3NpdmUpICU+JQogIGdhdGhlcihtZXRyaWMsIHZhbHVlLCA3OjExKSAlPiUgICAgICAjIG5vdCBpbmNsdWRlIEZGUmVnIHdoaWNoIGlzIGluIGNvbHVtbiAxMQogIG11dGF0ZSh2YWx1ZSA9IGlmX2Vsc2UoaXMubmEodmFsdWUpLCBhcy5pbnRlZ2VyKDApLCBhcy5pbnRlZ2VyKHZhbHVlKSkpICU+JQogIGRyb3BfbmEoKSAlPiUgICAjIGFjdHVhbGx5LCBkcm9wIGZpcnN0IHdvcmQgaW4gYSBzZW50ZW5jZQogIGdyb3VwX2J5KHRleHRfaWQsIHdvcmRfdGV4dF9pZHgsIHNlbnRfaWQsIHdvcmRfaWR4LCB3b3JkLCBtZXRyaWMpICU+JQogIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCiMgVmlldyhwcm92b19leWV0cmFja2luZ19kZikKcHJvdm9fZXlldHJhY2tpbmdfZGYKCmBgYAoKCmBgYHtyfQpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2V5ZXRyYWNraW5nX2RmLCBwcm92b19tb2RlbGluZ19kZiwgYnk9YygidGV4dF9pZCIsICJzZW50X2lkIiwgIndvcmRfaWR4IikpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGFycmFuZ2UodGV4dF9pZCwgc2VudF9pZCwgd29yZF9pZHgpCnByb3ZvX2RmCgpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2RmLCBtb3RyX3Byb3ZvX2RmLCBieT1jKCJ0ZXh0X2lkIiwgIndvcmRfdGV4dF9pZHgiLCAibWV0cmljIikpICU+JQogIHJlbmFtZShleWV0cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBhcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KSAlPiUKICBmaWx0ZXIod29yZC54ID09IHdvcmQpICU+JSAgICAgICN3b3JkLnkgaGFzIG5vIGNhcHRpY2FsIHdvcmQKICBkcGx5cjo6c2VsZWN0KC13b3JkLngsIC13b3JkLnkpICU+JQogIG11dGF0ZShtb3RyX291dGxpZXIgPSBpZl9lbHNlKG1vdHJfdmFsdWUgPiAobWVhbihtb3RyX3ZhbHVlKSArIDMgKiBzZChtb3RyX3ZhbHVlKSApLCBULCBGKSkgJT4lCiAgZmlsdGVyKG1vdHJfb3V0bGllciA9PSBGKSAlPiUgICAgICMgY2xlYXIgb3V0bGllciAtPiAxMyB3YXMgZmlsdGVyZWQuCiAgZ2F0aGVyKG1lYXN1cmUsIHZhbHVlLCBjKCJleWV0cl92YWx1ZSIsICJtb3RyX3ZhbHVlIikpICAlPiUKICAjIGZpbHRlcihtZXRyaWMgIT0gImZpcnN0X2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKQoKIyBWaWV3KHByb3ZvX2RmKQoKCmBgYAoKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgZmFjZXRfd3JhcCgufm1lYXN1cmUpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL2RlbnNpdHkucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAibW90cl92YWx1ZSIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX2RlbnNpdHkoKSArCiAgICB4bGFiKCJSZWFkaW5nIE1lYXN1cmUiKSArCiAgICBnZ3RpdGxlKCJEZW5zaXR5IHBsb3Qgb2YgdmFsdWUgZm9yIG1vdHIiKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAiZXlldHJfdmFsdWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogICAgZ2d0aXRsZSgiRGVuc2l0eSBwbG90IG9mIHZhbHVlIGZvciBleWV0ciIpCmBgYAoKCmBgYHtyfQpnZF9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJ0b3RhbF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpClZpZXcoZ2RfZGYpCgpjb3IudGVzdChnZF9kZiRleWV0cl92YWx1ZSwgZ2RfZGYkbW90cl92YWx1ZSkgICAgICAgICAgI2NvciByaXNlIGZyb20gMC41MjkgdG8gMC41OTg5IAoKcHJvdm9fZGYgJT4lCiAgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtb3RyX3ZhbHVlLCB5PWV5ZXRyX3ZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF93cmFwKC5+bWV0cmljLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9tZXRyaWNfY29yLnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKCmBgYAoKYGBge3J9CmdkX2RmXzIgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMiRleWV0cl92YWx1ZSwgZ2RfZGZfMiRtb3RyX3ZhbHVlKQpgYGAKCmBgYHtyfQpnZF9kZl8zID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMyRleWV0cl92YWx1ZSwgZ2RfZGZfMyRtb3RyX3ZhbHVlKQpgYGAKCgpgYGB7cn0KZ2RfZGZfNCA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnb19wYXN0X3RpbWUiKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfNCRleWV0cl92YWx1ZSwgZ2RfZGZfNCRtb3RyX3ZhbHVlKQoKYGBgCmBgYHtyfQpwcm92b19kZiAlPiUKICBnYXRoZXIod29yZF9wcm9wLCB3b3JkX3Byb3BfdmFsLCBjKCJmcmVxIiwgImxlbiIsICJzdXJwIikpICU+JQogIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT13b3JkX3Byb3BfdmFsLCBjb2xvciA9IG1lYXN1cmUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKHdvcmRfcHJvcH5tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi93b3JkX3Byb3BfY29tcHMucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMykKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHk9ZnJlcSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgZmFjZXRfZ3JpZChtZXRyaWN+bWVhc3VyZSwgc2NhbGVzPSJmcmVlIikgKwogICAgZ2VvbV9zbW9vdGgoKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1zdXJwLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKCgojIyBTaGFwZSBvZiBzdXJwcmlzYWwgLyBSVCByZWxhdGlvbnNoaXAKCmBgYHtyfQoKZml0X2dhbV9pbm5lciA9IGZ1bmN0aW9uKGJvb3RzdHJhcF9zYW1wbGUsIG1lYW5fcHJlZGljdG9ycykgewogIAogIGRmID0gYm9vdHN0cmFwX3NhbXBsZSRkYXRhCiAgd2VpZ2h0cyA9IHRhYnVsYXRlKGFzLmludGVnZXIoYm9vdHN0cmFwX3NhbXBsZSksIG5yb3coZGYpKQogIAogIG0gPSBnYW0ocHN5Y2hvbWV0cmljIH4gcyhzdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHMocHJldl9zdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHRlKGZyZXEsIGxlbiwgYnMgPSAnY3InKSArIHRlKHByZXZfZnJlcSwgcHJldl9sZW4sIGJzID0gJ2NyJyksIGRhdGEgPSBkZiwgd2VpZ2h0cyA9IHdlaWdodHMpCiAgdGVybXNfdG9fcHJlZGljdCA9IGMoInMoc3VycCkiLCAicyhwcmV2X3N1cnApIikKICAKICBuZXdkYXRhID0gZGF0YS5mcmFtZShzdXJwPXNlcSgwLDIwLGJ5PTAuMSksIHByZXZfc3VycD1tZWFuX3ByZWRpY3RvcnMkc3VycCwKICAgICAgICAgICAgICAgICAgICAgICBmcmVxPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2ZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgbGVuPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2xlbj1tZWFuX3ByZWRpY3RvcnMkZnJlcSkKCiAgIyBSZXR1cm5zIGEgbWF0cml4IE5fc2FtcGxlcyAqIE5fdGVybXMuCiAgcGVyX3Rlcm1fcHJlZGljdGlvbnMgPSBwcmVkaWN0KG0sIG5ld2RhdGE9bmV3ZGF0YSwgdGVybXM9dGVybXNfdG9fcHJlZGljdCwgdHlwZT0idGVybXMiKQoKICAjIEFkZGl0aXZlIG1vZGVsIC0tIHN1bSBhY3Jvc3MgcHJlZGljdG9yIHJlc3BvbnNlIGNvbnRyaWJ1dGlvbnMgKG1hdHJpeCBjb2x1bW5zKS4KICBwcmVkaWN0aW9ucyA9IHJvd1N1bXMocGVyX3Rlcm1fcHJlZGljdGlvbnMpCgogIHJldHVybihuZXdkYXRhICU+JSBtdXRhdGUoeT1wcmVkaWN0aW9ucykpCn0KCmZpdF9nYW0gPSBmdW5jdGlvbihkZiwgbWVhbl9wcmVkaWN0b3JzLCBhbHBoYT0wLjA1KSB7CiAgIyBCb290c3RyYXAtcmVzYW1wbGUgZGF0YQogIGJvb3RfbW9kZWxzID0gZGYgJT4lIGJvb3RzdHJhcHModGltZXM9MTApICU+JSAKICAgIyBGaXQgYSBHQU0gYW5kIGdldCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzYW1wbGUKICAgIG11dGF0ZShzbW9vdGhlZD1tYXAoc3BsaXRzLCBmaXRfZ2FtX2lubmVyLCBtZWFuX3ByZWRpY3RvcnM9bWVhbl9wcmVkaWN0b3JzKSkKICAKICAjIEV4dHJhY3QgbWVhbiBhbmQgNSUgYW5kIDk1JSBwZXJjZW50aWxlIHktdmFsdWVzIGZvciBlYWNoIHN1cnByaXNhbCB2YWx1ZQogIHJlc3VsdCA9IGJvb3RfbW9kZWxzICU+JSAKICAgIHVubmVzdChzbW9vdGhlZCkgJT4lIAogICAgZHBseXI6OnNlbGVjdChzdXJwLCB5KSAlPiUgCiAgICBncm91cF9ieShzdXJwKSAlPiUgCiAgICAgIHN1bW1hcmlzZSh5X2xvd2VyPXF1YW50aWxlKHksIGFscGhhIC8gMiksIAogICAgICAgICAgICAgICAgeV91cHBlcj1xdWFudGlsZSh5LCAxIC0gYWxwaGEgLyAyKSwKICAgICAgICAgICAgICAgIHk9bWVhbih5KSkgJT4lIAogICAgdW5ncm91cCgpCiAgCiAgcmV0dXJuIChyZXN1bHQpCn0KCgpgYGAKCgpgYGB7cn0KCmdhbV9tb2RlbGluZ19kZiA9IHByb3ZvX2RmICU+JQogIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkgJT4lCiAgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUKICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCgoKc21vb3Roc19kZiA9IGRhdGEuZnJhbWUoKQoKbWV0cmljcyA9IGMoImdhemVfZHVyYXRpb24iLCAidG90YWxfZHVyYXRpb24iLCAiZ29fcGFzdF90aW1lIiwgImZpcnN0X2R1cmF0aW9uIikKZm9yIChtIGluIG1ldHJpY3MpIHsKICBwcmludChwYXN0ZTAoIkZpdHRpbmcgbW9kZWwgZm9yICIsIG0pKQogIGR1bW15X2RmID0gZ2FtX21vZGVsaW5nX2RmICU+JSBmaWx0ZXIobWV0cmljID09IG0pCiAgbWVhbl9wcmVkaWN0b3JzID0gZHVtbXlfZGYgJT4lIHN1bW1hcmlzZShzdXJwID0gbWVhbihzdXJwKSwgbGVuID0gbWVhbihsZW4pLCBmcmVxID0gbWVhbihmcmVxKSkKICBzbW9vdGhzID0gZHVtbXlfZGYgJT4lIGZpdF9nYW0oLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCn0KCmBgYAoKCmBgYHtyfQoKIyBTdXJwcmlzYWwgY3VydmVzCiAgZ2dwbG90KCkgKwogICAgICBnZW9tX2xpbmUoZGF0YSA9IHNtb290aHNfZGYsIGFlcyh4PXN1cnAsIHk9eSwgY29sb3IgPSBwc3ljaG9tZXRyaWMpLCBzaXplPTAuNykgKwogICAgICBnZW9tX3JpYmJvbihkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9c3VycCwgeW1pbj15X2xvd2VyLCB5bWF4PXlfdXBwZXIsIGZpbGwgPSBwc3ljaG9tZXRyaWMpLCBhbHBoYT0wLjMsIHNpemU9MC41KSArCiAgICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9YygwLCAxMCwgMjApLCBicmVha3M9YygwLCAxMCwgMjApLCBtaW5vcl9icmVha3MgPSBOVUxMKSArCiAgICAgIGZhY2V0X3dyYXAocHN5Y2hvbWV0cmljfi4pICsKICAgICAgeWxhYigiU2xvd2Rvd24gZHVlIHRvIFN1cnByaXNhbCAobXMpIikgKwogICAgICB4bGFiKCJTdXJwcmlzYWwgb2YgV29yZCIpICsKICAgICAgZ2d0aXRsZSgiUmVsYXRpb25zaGlwIGJldHdlZW4gTW9UUiBUaW1lcyBhbmQgU3VycHJpc2FsIikKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkKICApCgoKCgpgYGAKCiMgVGFyZ2V0ZWQgRXZhbHVhdGlvbiBEYXRhCgoKCmBgYHtyfQoKbW90cl9hdHRhY2hfZGYgPSBtb3RyX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJBdHRhY2htZW50IikgJT4lCiAgcmVuYW1lKCBpdGVtX2lkID0gcGFyYV9ucikgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5pbnRlZ2VyKGl0ZW1faWQpKSAlPiUKICBtdXRhdGUoY29uZF9pZCA9IGFzLmZhY3Rvcihjb25kX2lkKSkgJT4lCiAgbXV0YXRlKGNvbmRfaWQgPSBpZl9lbHNlKGNvbmRfaWQgPT0gMSwgIk5vIENvbW1hIiwgIkNvbW1hIikpICU+JQogIGZpbHRlcihtZXRyaWMgIT0gIkZQUmVnIikgJT4lCgogIGZpbHRlcighIChpdGVtX2lkID09IDQgJiBjb25kX2lkID09ICJObyBDb21tYSIpICkgJT4lICMganVzdCBiZWNhdXNlIG9mIGFsaWdubWVudCBpc3N1ZXMgZm9yIG5vdwogIAogIG11dGF0ZShjcml0ID0gaWZfZWxzZSh3b3JkID09ICJhbmQiLCB3b3JkX25yLCBhcy5pbnRlZ2VyKDApICkpICU+JQogIGdyb3VwX2J5KGNvbmRfaWQsIGl0ZW1faWQpICU+JQogICAgbXV0YXRlKGNyaXQgPSB1bmlxdWUoY3JpdClbMl0pICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUod29yZF9uciA9IHdvcmRfbnIgLSBjcml0KQoKCmFnZ19tb3RyX2F0dGFjaF9kZiA9IG1vdHJfYXR0YWNoX2RmICU+JQogIGZpbHRlcih3b3JkX25yID49IC0yLCB3b3JkX25yIDwgNikgJT4lCiAgZ3JvdXBfYnkoY29uZF9pZCwgd29yZF9uciwgbWV0cmljKSAlPiUKICAgIHN1bW1hcmlzZSggbSA9IG1lYW4odmFsdWUpLAogICAgICAgICAgICAgICBzZCA9IHN0ZC5lcnJvcih2YWx1ZSksCiAgICAgICAgICAgICAgIHVwcGVyID0gbSArIDEuOTYgKiBzZCwKICAgICAgICAgICAgICAgbG93ZXIgPSBtIC0gMS45OCAqIHNkLAogICAgICAgICAgICAgICBuID0gbigpKSAlPiUKICB1bmdyb3VwKCkKCmFnZ19tb3RyX2F0dGFjaF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB3b3JkX25yLCB5ID0gbSwgY29sb3IgPSBjb25kX2lkKSkgKwogICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMi41LCB4bWF4ID0gNS41LCB5bWluID0gMTAwLCB5bWF4ID0gODAwKSwgZmlsbD1hbHBoYSgid2hpdGUiLCAwKSwgY29sb3IgPSAiIzQ1ZWY3MCIsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWF4ID0gdXBwZXIsIHltaW4gPSBsb3dlciksIHdpZHRoID0gMC4zKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICAjZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdvcmQsIHkgPSBpZl9lbHNlKGNvbmRfaWQgPT0gIkNvbW1hIiwgMzAwMCwgMzUwMCkpLCBzaXplID0gMikgKwogICAgI2ZhY2V0X2dyaWQocGFyYV9ucn5jb25kX2lkKSArCiAgeWxhYigiUmVhZGluZyBUaW1lIikgKwogIHhsYWIoIkNvbmRpdGlvbiIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCJ0aGUiLCAibWFuIiwgImFuZCIsICJoaXMiLCAid2lmZSIsICJyYW4iLCAiYXdheSIsICJmcm9tIikpICsKICBmYWNldF9ncmlkKH5tZXRyaWMpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKQogICkKCiNnZ3NhdmUoIi4uL3Zpc3VhbGl6YXRpb24vYXR0YWNobWVudC5wbmciLCBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQoKYGBgCgpgYGB7cn0KCm9wdGlvbnMoSlVMSUFfSE9NRSA9ICIvQXBwbGljYXRpb25zL0p1bGlhLTEuOC5hcHAvQ29udGVudHMvUmVzb3VyY2VzL2p1bGlhL2Jpbi8iKQojIGxpYnJhcnkoamdsbW0pCiMgamdsbW1fc2V0dXAoKQoKYXR0YWNoX2xtX2RmID0gbW90cl9hdHRhY2hfZGYgJT4lCiAgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JQogIGZpbHRlcih3b3JkX25yID09IDMpICU+JQogIG11dGF0ZShpdGVtX2lkID0gYXMuZmFjdG9yKGl0ZW1faWQpLAogICAgICAgICBzdWJqID0gYXMuZmFjdG9yKHN1YmopKQoKbSA9IGF0dGFjaF9sbV9kZiAlPiUKICBsbWVyKHZhbHVlIH4gY29uZF9pZCArIChjb25kX2lkIHwgaXRlbV9pZCkgKyAoY29uZF9pZCB8IHN1YmopLCBkYXRhPS4pCgpzdW1tYXJ5KG0pCgpgYGAKCgoKCg==